package com.gooluke.spring.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.HashSet;
import java.util.Set;

public class AnnotationUtils {

    /**
     * 检查元素（类/方法/字段）是否直接或间接（通过元注解）包含指定注解
     */
    public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) {
        return findAnnotation(element, annotationType) != null;
    }

    /**
     * 查找元素（类/方法/字段）上的指定注解（包括元注解）
     */
    public static <A extends Annotation> A findAnnotation(AnnotatedElement element, Class<A> annotationType) {
        // 1. 先检查直接声明的注解
        A annotation = element.getDeclaredAnnotation(annotationType);
        if (annotation != null) {
            return annotation;
        }

        // 2. 递归检查元注解
        for (Annotation metaAnnotation : element.getDeclaredAnnotations()) {
            Class<? extends Annotation> metaAnnotationType = metaAnnotation.annotationType();
            // 跳过 Java 标准注解（如 @Target, @Retention 等，因为它们不是业务注解）
            if (metaAnnotationType.getName().startsWith("java.lang.annotation")) {
                continue;
            }
            A foundAnnotation = findAnnotation(metaAnnotationType, annotationType);
            if (foundAnnotation != null) {
                return foundAnnotation;
            }
        }

        return null;
    }

    /**
     * 获取元素（类/方法/字段）上的所有指定注解（包括元注解）
     */
    public static <A extends Annotation> Set<A> findAllAnnotations(AnnotatedElement element, Class<A> annotationType) {
        Set<A> annotations = new HashSet<>();
        collectAnnotations(element, annotationType, annotations);
        return annotations;
    }

    private static <A extends Annotation> void collectAnnotations(
            AnnotatedElement element, Class<A> annotationType, Set<A> annotations) {
        // 1. 检查直接声明的注解
        A annotation = element.getDeclaredAnnotation(annotationType);
        if (annotation != null) {
            annotations.add(annotation);
        }

        // 2. 递归检查元注解
        for (Annotation metaAnnotation : element.getDeclaredAnnotations()) {
            Class<? extends Annotation> metaAnnotationType = metaAnnotation.annotationType();
            if (metaAnnotationType.getName().startsWith("java.lang.annotation")) {
                continue;
            }
            collectAnnotations(metaAnnotationType, annotationType, annotations);
        }
    }
}